import datetime
import os
from operator import itemgetter

from django.db.models.functions import Cast
from django.http import Http404, FileResponse, HttpResponse
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.decorators import api_view
from rest_framework.parsers import FileUploadParser
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
from django.db.models import Q, F
from django.db import models
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector

from proj.settings import BASE_DIR
from user.models import ProjUser
from .exercise_graph import get_ab_graph, get_t_graph
from .pagination import ExerciseListPagination
from .parse import parse_choice_questions, parse_blank_questions
from .serializers import QuestionSerializer, QuestionDetailSerializer, QuestionGroupSerializer, \
    QuestionGroupDetailSerializer, LogSerializer
from .models import Question, QuestionGroup, UserExerciseLog
import json


class ExerciseList(APIView):
    pagination_class = ExerciseListPagination

    def get(self, request, format=None):
        """
        获取题目列表的api

        可以在请求中指定查询参数`q=xxx`来执行针对题目列表的模糊查询

        返回的数据格式为：

        ```json
        {
            "links": { // 分页相关的字段
                "next": null,
                "previous": null
            },
            "count": 1, // 统计查询结果数目的字段
            "results": [ // 查询结果字段
                {
                    "id": 1,
                    "title": "选择选择",
                    "label": "数学",
                    "type": "CQ"
                }
            ]
        }
        ```
        """
        keyword = request.GET.get('q')
        is_self = request.GET.get('self')
        if is_self == 'true':
            if keyword:
                questions = Question.objects.filter(
                    Q(id__icontains=keyword) |
                    Q(stem__icontains=keyword) |
                    Q(name__icontains=keyword) |
                    Q(label__icontains=keyword),
                    creator=request.user
                )
            else:
                questions = Question.objects.filter(creator=request.user)
        else:
            user = ProjUser.objects.get(username=request.user.username)
            if keyword:
                if not user.is_admin:
                    questions = Question.objects.filter(
                        Q(id__icontains=keyword) |
                        Q(stem__icontains=keyword) |
                        Q(name__icontains=keyword) |
                        Q(label__icontains=keyword),
                        authority=0
                    )
                else:
                    questions = Question.objects.filter(
                        Q(id__icontains=keyword) |
                        Q(stem__icontains=keyword) |
                        Q(name__icontains=keyword) |
                        Q(label__icontains=keyword)
                    )
            else:
                if not user.is_admin:
                    questions = Question.objects.filter(authority=0)
                else:
                    questions = Question.objects.all()

        paginator = self.pagination_class()
        paginated_questions = paginator.paginate_queryset(questions, request)
        serializer = QuestionSerializer(paginated_questions, many=True)
        # serializer = QuestionSerializer(questions, many=True)
        return paginator.get_paginated_response(serializer.data)

    # def get_permissions(self):
    #     return [IsAuthenticated()]


class ExerciseDetail(APIView):
    def get_question(self, id):
        try:
            return Question.objects.get(id=id)
        except Question.DoesNotExist:
            raise Http404

    def get(self, request, id, format=None):  # 获取题目细节信息
        """
        通过在url末端提供的id来获取题目细节信息的api

        返回的json数据格式为:

        选择题：
        ```json
        "data": {
            "type":"CQ", // 这里意思是'Choice Question'
            "authority":0, // 题目的可见性，0为公开，1为不公开
            "stem": "这道题的题干",
            "name": "题目名称", // 选填，可以不提供该字段
            "label": "题目标签", // 10字符内
            "analysis": "题目解析",
            "choice": {
                "id": 1, // 该部分在表中的id，可以不用管
                "type": 0, // (0:单选题;1:多选题;2:不定项选择题)
                "options_cnt": 11, // 选项数
                "options": '{"A": "选项A", "B\": "选项B", "C": "选项C"}',
                "ans_cnt": 1, // 选项数
                "ans": "[
                    "A",
                    "B"
                ]" // 类似这样的json数组字符串
            },
            "id": 1, // 这道题的id
            "is_banned": false, // 这道题是否被封禁
            "create_time": "2023-07-23T17:33:00", // 创建时间
            "update_time": "2023-07-23T19:39:18.633738", // 更新时间
            "creator": 1, // 创建者的id
            "blank":null
        }
        ```
        填空题：
        ```json
        "data": {
            "type":"FBQ", // 这里意思是'Fill in Blank Question'
            "authority":0, // 题目的可见性，0为公开，1为不公开
            "stem": "这道题的题干",
            "name": "题目名称", // 选填，可以不提供该字段
            "label": "题目标签", // 10字符内
            "analysis": "题目解析",
            "blank": {
                "id": 1, // 该部分在表中的id，可以不用管
                "blank_cnt": 2, // 题目的空数
                "ans": "[
                    "blank1",
                    "blank2"
                ]"
            }
            "id": 1, // 这道题的id
            "is_banned": false, // 这道题是否被封禁
            "create_time": "2023-07-23T17:33:00", // 创建时间
            "update_time": "2023-07-23T19:39:18.633738", // 更新时间
            "creator": 1, // 创建者的id
            "choice":null
        }
        ```

        """
        question = self.get_question(id)
        serializer = QuestionDetailSerializer(question)
        return Response(serializer.data)

    def post(self, request, id, format=None):  # 做题，返回评测结果
        """
        上传题目答案并返回题目评测结果的api

        首先通过url的末端提供的id来确定用户所做的题目

        上传的选择题答案数据:
        ```json
        "data":{
            "ans": [
                "A",
                "B"
            ]
        }
        ```
        上传的填空题答案数据:

        返回的数据格式为:

        ```json
        "data":{
            "ans": [
                "blank1",
                "blank2"
            ]
        }
        ```

        ```json
        "data": {
            "message": "WA" // 如果正确会返回"AC"
        }
        ```

        """
        user_ans = json.loads(request.data.get('ans'))
        question = self.get_question(id)
        if question.type == 'CQ':
            ans = json.loads(question.choice.ans)
            if sorted(user_ans) != sorted(ans):
                UserExerciseLog.objects.create(submit_user=ProjUser.objects.get(username=request.user.username)
                                               , submit_question=question, submit_time=datetime.datetime.now()
                                               , is_correct=False, wrong_ans=json.dumps(sorted(user_ans)))
                return Response({"message": "WA"})
        elif question.type == 'FBQ':
            ans = json.loads(question.blank.ans)
            if user_ans != ans:
                UserExerciseLog.objects.create(submit_user=ProjUser.objects.get(username=request.user.username)
                                               , submit_question=question, submit_time=datetime.datetime.now(),
                                               is_correct=False, wrong_ans=json.dumps(user_ans))
                return Response({"message": "WA"})
        UserExerciseLog.objects.create(submit_user=ProjUser.objects.get(username=request.user.username)
                                       , submit_question=question, submit_time=datetime.datetime.now(),
                                       is_correct=True)
        return Response({"message": "AC"})


class ExerciseGroupList(APIView):
    pagination_class = ExerciseListPagination

    def get(self, request, format=None):
        """
        获取题组列表的api

        可以在请求中指定查询参数`q=xxx`来执行针对全体题组的模糊查询

        返回json格式为如下这样的json数组:

        ```json
        [
            {
                "id": 2,
                "name": "test",
                "creator_name": "admin",
                "create_time": "2023-07-23T23:22:00"
            }
        ]
        ```
        """
        keyword = request.GET.get('q')
        is_self = request.GET.get('self')
        if is_self == 'true':
            if keyword:
                question_groups = QuestionGroup.objects.filter(
                    Q(id__icontains=keyword) |
                    Q(name__icontains=keyword) |
                    Q(describe__icontains=keyword),
                    creator=request.user
                )
            else:
                question_groups = QuestionGroup.objects.filter(creator=request.user)
        else:
            if keyword:
                question_groups = QuestionGroup.objects.filter(
                    Q(id__icontains=keyword) |
                    Q(name__icontains=keyword) |
                    Q(describe__icontains=keyword),
                    authority=0
                )
            else:
                question_groups = QuestionGroup.objects.filter(authority=0)

        paginator = self.pagination_class()
        paginated_groups = paginator.paginate_queryset(question_groups, request)
        serializer = QuestionGroupSerializer(paginated_groups, many=True)
        return paginator.get_paginated_response(serializer.data)



class ExerciseGroupDetail(APIView):
    # permission_classes = [UserQuestionSelfEdit, ]
    #authentication_classes = [SessionAuthentication, ]
    def get_question_group(self, id):
        try:
            return QuestionGroup.objects.get(id=id)
        except QuestionGroup.DoesNotExist:
            raise Http404

    def get(self, request, id, format=None):
        """
        通过url末尾指定的id来获取指定题组的详细信息的api

        获取到的json数据格式如下:

        ```json
        {
            "id": 2,
            "questions": [ // 描述问题的json数组
                {
                    "id": 1,
                    "title": "选择选择",
                    "label": "数学",
                    "type": "CQ"
                }
            ],
            "authority": 0,
            "name": "test",
            "describe": "测试测试测试",
            "is_banned": false, // 是否被封禁
            "create_time": "2023-07-23T23:22:00",
            "creator": 1, // 创建者的id
            "user_groups": [ // 用户组中的用户的id
                4
            ]
        }
        ```
        """
        question_group = self.get_question_group(id)
        serializer = QuestionGroupDetailSerializer(question_group)
        return Response(serializer.data)

    def delete(self, request, id):
        """
        通过url末尾指定的id来删除指定题组的详细信息的api
        """
        try:
            question_group = QuestionGroup.objects.get(id=id)
        except QuestionGroup.DoesNotExist:
            return Response({'error': 'Question group does not exist.'}, status=status.HTTP_404_NOT_FOUND)

        if question_group.creator != request.user:
            return Response({'error': 'You are not authorized to delete this question group.'},
                            status=status.HTTP_403_FORBIDDEN)

        question_group.delete()

        return Response({'message': 'Question group deleted successfully.'}, status=status.HTTP_204_NO_CONTENT)


@api_view(['GET'])
def get_recommend(request):
    """
    向用户推荐三天内错的最多的十道题
    """
    user = ProjUser.objects.get(username=request.user.username)
    # 获取该用户三天内错的最多的十道题
    #   获取三天内的错题列表
    start_time = datetime.datetime.now() - datetime.timedelta(days=2)
    end_time = datetime.datetime.now()
    user_wa_list = list(user.submit_logs.filter(is_correct=False, submit_time__range=[start_time, end_time]))
    if len(user_wa_list) == 0:
        return Response({'message': '您近期正确率很高，请继续保持'}, status=status.HTTP_400_BAD_REQUEST)
    #   获取这些题中错的最多的
    d = {}
    for q in user_wa_list:
        if q.submit_question_id not in d:
            d[q.submit_question_id] = 1
        else:
            d[q.submit_question_id] = d[q.submit_question_id] + 1
    sorted_d = dict(sorted(d.items(), key=itemgetter(1)))
    print(sorted_d)
    cnt = 0
    usual_wa_list = []
    for i_d, time in sorted_d.items():
        if cnt == 10:
            break
        usual_wa_list.append(i_d)
    recommend_list = Question.objects.filter(id__in=usual_wa_list)
    s = QuestionSerializer(recommend_list, many=True)
    return Response(s.data, status=status.HTTP_200_OK)


@api_view(['GET'])
def get_log_list(request):
    """
    获取个人做题记录列表的api
    """
    user = ProjUser.objects.get(username=request.user.username)
    s = LogSerializer(user.submit_logs, many=True)
    return Response(s.data, status=status.HTTP_200_OK)


class ExerciseUploadByFile(APIView):
    def post(self, request):
        parser_classes = (FileUploadParser,)
        file_obj = request.data.get('file')
        q_type = request.data.get('type', '')
        authority = request.data.get('authority', '')
        name = request.data.get('name', '')
        label = request.data.get('label', '')
        choice_type = request.data.get('choice_type', '')

        if not q_type or not label:
            return Response({'error': 'Type and label are required'},
                            status=status.HTTP_400_BAD_REQUEST)
        if q_type == 'CQ' and not choice_type:
            return Response({'error': 'Choice Question\'s type are required'},
                            status=status.HTTP_400_BAD_REQUEST)
        if not file_obj:
            return Response({'error': 'No file received'},
                            status=status.HTTP_400_BAD_REQUEST)

        content = file_obj.read().decode('utf-8')
        parse_data = None
        if q_type == 'CQ':
            parse_data = parse_choice_questions(content, authority, name, label, choice_type)
            print(parse_data)
        elif q_type == 'FBQ':
            parse_data = parse_blank_questions(content, authority, name, label)
            print(parse_data)
        else:
            return Response({'error': 'Question type error.'}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'message': 'File uploaded successfully', 'parse_data': parse_data}, status=status.HTTP_200_OK)


@api_view(['GET', ])
def get_ability_graph(request):
    """
    调用该api用户可以获取自己近十天的做题正确率数据统计图
    """
    name = get_ab_graph(request.user.username)
    path = BASE_DIR / 'pics' / name
    with open(path, 'rb') as file:
        response = HttpResponse(file.read(), content_type='image/png')
        response['Content-Disposition'] = f'attachment; filename="{name}"'
    os.remove(path)
    return response


@api_view(['GET', ])
def get_total_graph(request):
    """
    调用该api用户可以获取自己近十天的做题总数数据统计图
    """
    name = get_t_graph(request.user.username)
    path = BASE_DIR / 'pics' / name
    with open(path, 'rb') as file:
        response = HttpResponse(file.read(), content_type='image/png')
        response['Content-Disposition'] = f'attachment; filename="{name}"'
    os.remove(path)
    return response

